//-----------------------------------------------------------------------------
// Filename: SIPDialogue.cs
//
// Description: Base class for SIP dialogues. 
// 
// History:
// 20 Oct 2005	Aaron Clauson	Created.
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2006 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using mimosa.SIP;
using log4net;
using mimosa.Utilities;

namespace mimosa.SIP.Dialogue
{
    public delegate void SIPDialogueRemoved(SIPDialogue sipDialogue);
    
    public enum SIPDialogueStateEnum
    {
        Unknown = 0,
        Early = 1,
        Confirmed = 2,
    }
    
    /// <summary>
    /// See "Chapter 12 Dialogs" in RFC3261.
    /// </summary>
    public class SIPDialogue
	{
		protected static ILog logger = AppState.logger;

		protected static string m_CRLF = SIPConstants.CRLF;
		protected static string m_sipVersion = SIPConstants.SIP_VERSION_STRING;

        protected string m_callId;
        public string CallId
        {
            get { return m_callId; }
        }

        protected SIPRouteSet m_routeSet;       // List of routes in the INVITE request arriving at the UAS.
        public SIPRouteSet RouteSet
        {
            get { return m_routeSet; }
        }

        protected SIPUserField m_localUser;     // To header for a UAS, From header for a UAC.
        public SIPUserField LocalUserField
        {
            get { return m_localUser; }
        }

        protected int m_localCSeq;              // CSeq used by the local UA for sending requests.
        public int LocalCSeq
        {
            get { return m_localCSeq; }
        }

        protected SIPUserField m_remoteUser;    // To header for a UAC, From header for a UAS.
        public SIPUserField RemoteUserField
        {
            get { return m_remoteUser; }
        }
        
        protected int m_remoteCSeq = -1;        // CSeq being used by the remote UA for sending requests.
        public int RemoteCSeq
        {
            get { return m_remoteCSeq; }
        }

        protected SIPURI m_remoteTarget;        // This will be the Contact URI in the INVITE request or in the 2xx INVITE response and is where subsequent dialogue requests should be sent.
        public SIPURI RemoteTarget
        {
            get { return m_remoteTarget; }
        }

        protected bool m_secure = false;

        public string DialogueId
        {
            get { return m_callId; }
        }

        public SIPRouteSet DialogueRouteSet
        {
            get { return m_routeSet; }
        }

        public event SIPDialogueRemoved DialogueRemoved;
        public SIPDialogueStateEnum DialogueState = SIPDialogueStateEnum.Unknown;

        private static Dictionary<string, SIPDialogue> m_dialogues = new Dictionary<string, SIPDialogue>();                 // [<callid>,<dialogue>].

		public SIPDialogue(
            string callId, 
            SIPRouteSet routeSet, 
            SIPUserField localUser,
            int localCSeq,
            SIPUserField remoteUser,
            int remoteCSeq,
            SIPURI remoteTarget)
		{
            m_callId = callId;
            m_routeSet = routeSet;
            m_localUser = localUser;
            m_localCSeq = localCSeq;
            m_remoteUser = remoteUser;
            m_remoteCSeq = remoteCSeq;
            m_remoteTarget = remoteTarget;
        }

        //public virtual void ResponseReceived(SIPResponse sipResponse, IPEndPoint inEndPoint, SIPChannel sipChannel)
        //{}

        //public virtual void RequestReceived(SIPRequest sipRequest, IPEndPoint inEndPoint, SIPChannel sipChannel)
        //{}

        public static void AddDialogue(SIPDialogue dialogue)
        {
            if (!m_dialogues.ContainsKey(dialogue.m_callId))
            {
                logger.Debug("Adding new dialogue for " + dialogue.m_callId + ".");
                m_dialogues.Add(dialogue.m_callId, dialogue);
            }
            else
            {
                throw new ApplicationException("SIP dialogue could not be added as it already exists.");
            }
        }

        public static void RemoveDialogue(string dialogueId)
        {
            if (m_dialogues.ContainsKey(dialogueId))
            {
                logger.Debug("Removing dialogue for " + dialogueId + ".");
                SIPDialogue dialogue = m_dialogues[dialogueId];
                m_dialogues.Remove(dialogueId);

                dialogue.FireDialogueRemovedEvent();
            }
            else
            {
                throw new ApplicationException("SIP dialogue could not be removed as it does not exist");
            }
        }

        public static SIPDialogue GetDialogue(string dialogueId)
        {
            if (m_dialogues.ContainsKey(dialogueId))
            {
                return m_dialogues[dialogueId];
            }
            else
            {
                return null;
            }
        }

        public static bool DialogueExists(string dialogueId)
        {
            return m_dialogues.ContainsKey(dialogueId);
        }

        public void FireDialogueRemovedEvent()
        {
            if (DialogueRemoved != null)
            {
                try
                {
                    DialogueRemoved(this);
                }
                catch (Exception excp)
                {
                    logger.Error("Exception FireDialogueRemovedEvent. " + excp.Message);
                }
            }
        }
	}
}
